package com.youlu.campus.common.utils;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import lombok.extern.slf4j.Slf4j;

import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.*;

/*
*
   时间工具类
 * 由于为了以后使用方便,所有方法的返回类型都设为了 java.util.Date 请在使用时根据自己的需要进行日期格式化处理,如:
 *
 * import java.text.SimpleDateFormat;SimpleDateFormat simpleDateFormat = new
 * SimpleDateFormat("yyyy-MM-dd HH:mm:ss");String todayBegin =
 * simpleDateFormat.format
 * (DateUtils.getDayBegin());System.out.println(todayBegin );//输出结果为2017-10-26
 * 时间控件
 * 参考:https://www.bbsmax.com/A/LPdoM9a2J3/
 * 00:00:00
 */
@Slf4j
public class TimeUtils {
    /**
     * 获取分钟的开始时间
     *
     * @param date
     * @return
     */
    public static java.util.Date getMinuteBegin(Date date) {
        String start = new SimpleDateFormat("yyyy-MM-dd HH:mm" + ":00").format(date);
        return parse(start, "yyyy-MM-dd HH:mm:ss");
    }

    /**
     * 获取分钟的结束时间
     *
     * @param date
     * @return
     */
    public static java.util.Date getMinuteEnd(Date date) {
        String start = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date);
        return parse(start, "yyyy-MM-dd HH:mm:ss");
    }

    /**
     * 获取小时的开始时间
     *
     * @param date
     * @return
     */
    public static java.util.Date getHourBegin(Date date) {
        String start = new SimpleDateFormat("yyyy-MM-dd HH" + ":00:00").format(date);
        return parse(start, "yyyy-MM-dd HH:mm:ss");
    }

    /**
     * 获取小时的结束时间
     *
     * @param date
     * @return
     */
    public static java.util.Date getHourEnd(Date date) {
        String end = new SimpleDateFormat("yyyy-MM-dd HH" + ":59:59").format(date);
        return parse(end, "yyyy-MM-dd HH:mm:ss");
    }

    // 获取当天的开始时间
    public static java.util.Date getDayBegin() {
        Calendar cal = new GregorianCalendar();
        cal.set(Calendar.HOUR_OF_DAY, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MILLISECOND, 0);
        return cal.getTime();
    }

    // 获取当天的结束时间
    public static java.util.Date getDayEnd() {
        Calendar cal = new GregorianCalendar();
        cal.set(Calendar.HOUR_OF_DAY, 23);
        cal.set(Calendar.MINUTE, 59);
        cal.set(Calendar.SECOND, 59);
        return cal.getTime();
    }

    // 获取昨天的开始时间
    public static Date getBeginDayOfYesterday() {
        Calendar cal = new GregorianCalendar();
        cal.setTime(getDayBegin());
        cal.add(Calendar.DAY_OF_MONTH, -1);
        return cal.getTime();
    }

    // 获取昨天的结束时间
    public static Date getEndDayOfYesterDay() {
        Calendar cal = new GregorianCalendar();
        cal.setTime(getDayEnd());
        cal.add(Calendar.DAY_OF_MONTH, -1);
        return cal.getTime();
    }

    // 获取明天的开始时间
    public static Date getBeginDayOfTomorrow() {
        Calendar cal = new GregorianCalendar();
        cal.setTime(getDayBegin());
        cal.add(Calendar.DAY_OF_MONTH, 1);
        return cal.getTime();
    }

    // 获取明天的结束时间
    public static Date getEndDayOfTomorrow() {
        Calendar cal = new GregorianCalendar();
        cal.setTime(getDayEnd());
        cal.add(Calendar.DAY_OF_MONTH, 1);
        return cal.getTime();
    }

    /**
     * 获取本周的开始时间
     *
     * @return
     */
    @SuppressWarnings("unused")
    public static Date getBeginDayOfWeek() {
        Date date = new Date();
        if (date == null) {
            return null;
        }
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        int dayofweek = cal.get(Calendar.DAY_OF_WEEK);
        if (dayofweek == 1) {
            dayofweek += 7;
        }
        cal.add(Calendar.DATE, 2 - dayofweek);
        return getDayStartTime(cal.getTime());
    }

    /**
     * 获取本周的结束时间
     *
     * @return
     */
    public static Date getEndDayOfWeek() {
        Calendar cal = Calendar.getInstance();
        cal.setTime(getBeginDayOfWeek());
        cal.add(Calendar.DAY_OF_WEEK, 6);
        Date weekEndSta = cal.getTime();
        return getDayEndTime(weekEndSta);
    }

    /**
     * 获取上周的开始时间
     *
     * @return
     */
    @SuppressWarnings("unused")
    public static Date getBeginDayOfLastWeek() {
        Date date = new Date();
        if (date == null) {
            return null;
        }
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        int dayofweek = cal.get(Calendar.DAY_OF_WEEK);
        if (dayofweek == 1) {
            dayofweek += 7;
        }
        cal.add(Calendar.DATE, 2 - dayofweek - 7);
        return getDayStartTime(cal.getTime());
    }

    /**
     * 获取上周的结束时间
     *
     * @return
     */
    public static Date getEndDayOfLastWeek() {
        Calendar cal = Calendar.getInstance();
        cal.setTime(getBeginDayOfLastWeek());
        cal.add(Calendar.DAY_OF_WEEK, 6);
        Date weekEndSta = cal.getTime();
        return getDayEndTime(weekEndSta);
    }

    /**
     * 获取本月的开始时间
     *
     * @return
     */
    public static Date getBeginDayOfMonth() {
        Calendar calendar = Calendar.getInstance();
        calendar.set(getNowYear(), getNowMonth() - 1, 1);
        return getDayStartTime(calendar.getTime());
    }

    /**
     * 获取月的开始时间
     *
     * @return
     */
    public static Date getBeginDayOfMonth(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.set(getYear(date), getMonth(date) - 1, 1);
        return getDayStartTime(calendar.getTime());
    }

    /**
     * 获取本月的结束时间
     *
     * @return
     */
    public static Date getEndDayOfMonth() {
        Calendar calendar = Calendar.getInstance();
        calendar.set(getNowYear(), getNowMonth() - 1, 1);
        int day = calendar.getActualMaximum(5);
        calendar.set(getNowYear(), getNowMonth() - 1, day);
        return getDayEndTime(calendar.getTime());
    }

    /**
     * 获取本月的结束时间
     *
     * @return
     */
    public static Date getEndDayOfMonth(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.set(getYear(date), getMonth(date) - 1, 1);
        int day = calendar.getActualMaximum(5);
        calendar.set(getYear(date), getMonth(date) - 1, day);
        return getDayEndTime(calendar.getTime());
    }

    /**
     * 获取上月的开始时间
     *
     * @return
     */
    public static Date getBeginDayOfLastMonth() {
        Calendar calendar = Calendar.getInstance();
        calendar.set(getNowYear(), getNowMonth() - 2, 1);
        return getDayStartTime(calendar.getTime());
    }

    /**
     * 获取上月的结束时间
     *
     * @return
     */
    public static Date getEndDayOfLastMonth() {
        Calendar calendar = Calendar.getInstance();
        calendar.set(getNowYear(), getNowMonth() - 2, 1);
        int day = calendar.getActualMaximum(5);
        calendar.set(getNowYear(), getNowMonth() - 2, day);
        return getDayEndTime(calendar.getTime());
    }


    /**
     * 获取本年的开始时间
     *
     * @return
     */
    public static Date getBeginDayOfYear() {
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.YEAR, getNowYear());
        cal.set(Calendar.MONTH, Calendar.JANUARY);
        cal.set(Calendar.DATE, 1);
        return getDayStartTime(cal.getTime());
    }

    /**
     * 获取本年的结束时间
     *
     * @return
     */
    public static java.util.Date getEndDayOfYear() {
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.YEAR, getNowYear());
        cal.set(Calendar.MONTH, Calendar.DECEMBER);
        cal.set(Calendar.DATE, 31);
        return getDayEndTime(cal.getTime());
    }

    /**
     * 获取某个日期开始时间,00:00:00
     *
     * @param d
     * @return
     */
    public static Timestamp getDayStartTime(Date d) {
        Calendar calendar = Calendar.getInstance();
        if (null != d)
            calendar.setTime(d);
        calendar.set(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH),
                calendar.get(Calendar.DAY_OF_MONTH), 0, 0, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        return new Timestamp(calendar.getTimeInMillis());
    }

    /**
     * 获取某个日期的结束时间
     */
    public static Timestamp getDayEndTime(Date d) {
        Calendar calendar = Calendar.getInstance();
        if (null != d)
            calendar.setTime(d);
        calendar.set(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH),
                calendar.get(Calendar.DAY_OF_MONTH), 23, 59, 59);
        calendar.set(Calendar.MILLISECOND, 999);
        return new Timestamp(calendar.getTimeInMillis());
    }

    // 获取今年是哪一年
    public static Integer getNowYear() {
        Date date = new Date();
        GregorianCalendar gc = (GregorianCalendar) Calendar.getInstance();
        gc.setTime(date);
        return Integer.valueOf(gc.get(1));
    }

    /**
     * 获取日期的年
     *
     * @param date
     * @return
     */
    public static Integer getYear(Date date) {
        GregorianCalendar gc = (GregorianCalendar) Calendar.getInstance();
        gc.setTime(date);
        return Integer.valueOf(gc.get(1));
    }

    /**
     * 获取日期月
     *
     * @param date
     * @return
     */
    public static int getMonth(Date date) {
        GregorianCalendar gc = (GregorianCalendar) Calendar.getInstance();
        gc.setTime(date);
        return gc.get(2) + 1;
    }

    // 获取本月是哪一月
    public static int getNowMonth() {
        Date date = new Date();
        GregorianCalendar gc = (GregorianCalendar) Calendar.getInstance();
        gc.setTime(date);
        return gc.get(2) + 1;
    }

    /**
     * 两个日期相减得到的天数
     *
     * @param beginDate
     * @param endDate
     * @return
     */
    public static int getDiffDays(Date beginDate, Date endDate) {
        if (beginDate == null || endDate == null) {
            throw new IllegalArgumentException("getDiffDays param is null!");
        }
        long diff = (endDate.getTime() - beginDate.getTime())
                / (1000 * 60 * 60 * 24);
        int days = new Long(diff).intValue();
        if (days == 0) {
            days++;
        }
        return days;
    }

    // 两个日期相减得到的毫秒数
    public static long dateDiff(Date beginDate, Date endDate) {
        long date1ms = beginDate.getTime();
        long date2ms = endDate.getTime();
        return date2ms - date1ms;
    }

    // 获取两个日期中的最大日期
    public static Date max(Date beginDate, Date endDate) {
        if (beginDate == null) {
            return endDate;
        }
        if (endDate == null) {
            return beginDate;
        }
        if (beginDate.after(endDate)) {
            return beginDate;
        }
        return endDate;
    }

    // 获取两个日期中的最小日期
    public static Date min(Date beginDate, Date endDate) {
        if (beginDate == null) {
            return endDate;
        }
        if (endDate == null) {
            return beginDate;
        }
        if (beginDate.after(endDate)) {
            return endDate;
        }
        return beginDate;
    }

    // 返回某月该季度的第一个月
    public static Date getFirstSeasonDate(Date date) {
        final int[] SEASON = {1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4};
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        int sean = SEASON[cal.get(Calendar.MONTH)];
        cal.set(Calendar.MONTH, sean * 3 - 3);
        return cal.getTime();
    }

    // 返回某个日期下几天的日期
    public static Date getNextDay(Date date, int i) {
        Calendar cal = new GregorianCalendar();
        cal.setTime(date);
        cal.set(Calendar.DATE, cal.get(Calendar.DATE) + i);
        return cal.getTime();
    }

    // 返回某个日期前几天的日期
    public static Date getFrontDay(Date date, int i) {
        Calendar cal = new GregorianCalendar();
        cal.setTime(date);
        cal.set(Calendar.DATE, cal.get(Calendar.DATE) - i);
        return cal.getTime();
    }

    // 获取某年某月到某年某月按天的切片日期集合(间隔天数的集合)
    @SuppressWarnings({"rawtypes", "unchecked"})
    public static List getTimeList(int beginYear, int beginMonth, int endYear,
                                   int endMonth, int k) {
        List list = new ArrayList();
        if (beginYear == endYear) {
            for (int j = beginMonth; j <= endMonth; j++) {
                list.add(getTimeList(beginYear, j, k));
            }
        } else {
            {
                for (int j = beginMonth; j < 12; j++) {
                    list.add(getTimeList(beginYear, j, k));
                }
                for (int i = beginYear + 1; i < endYear; i++) {
                    for (int j = 0; j < 12; j++) {
                        list.add(getTimeList(i, j, k));
                    }
                }
                for (int j = 0; j <= endMonth; j++) {
                    list.add(getTimeList(endYear, j, k));
                }
            }
        }
        return list;
    }

    /**
     * 获取小时开始时间
     *
     * @return
     */
    public static Date getEndOfHour(Date date) {
        SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH");
        String time = f.format(date);
        log.info(":>>> 当前的小时时间为为:{}", time);
        return parse(time, "yyyy-MM-dd HH");
    }

    /**
     * 获取某年某月按天切片日期集合(某个月间隔多少天的日期集合)
     *
     * @param beginYear
     * @param beginMonth
     * @param k
     * @return
     */
    @SuppressWarnings({"unchecked", "rawtypes"})
    public static List getTimeList(int beginYear, int beginMonth, int k) {
        List list = new ArrayList();
        Calendar begincal = new GregorianCalendar(beginYear, beginMonth, 1);
        int max = begincal.getActualMaximum(Calendar.DATE);
        for (int i = 1; i < max; i = i + k) {
            list.add(begincal.getTime());
            begincal.add(Calendar.DATE, k);
        }
        begincal = new GregorianCalendar(beginYear, beginMonth, max);
        list.add(begincal.getTime());

        return list;
    }

    /**
     * 获取当日零点
     *
     * @return
     */
    public static Date getDay0Time() {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        Date d = calendar.getTime();
        return d;
    }

    /**
     * 获取当日23点时间
     *
     * @param date
     * @return
     */
    public static Date getDay23Time(Date date) {
        Date d = new Date(date.getTime() + 24 * 60 * 60 * 1000L);
        return d;
    }

    /**
     * 日期转换:日期查询isodate
     *
     * @param date
     * @return
     */
    public static Date dateToISODate(Date date) {
        log.info(">>> dateToISODate with date:{}", date);
        //T代表后面跟着时间，Z代表UTC统一时间
        SimpleDateFormat format =
                new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
        format.setCalendar(new GregorianCalendar(new SimpleTimeZone(0, "GMT")));
        String isoDate = format.format(date);
        try {
            return format.parse(isoDate);
        } catch (ParseException e) {
            log.error(":>>> 转换时间偶误:{}", e.getMessage());
        }
        return null;
    }

    /**
     * 根据字符串获取时间
     *
     * @param format:yyyy-MM-dd
     * @param date:yyyy-MM-dd   00:00:00
     * @return
     */
    public static Date format(String format, String date) {

        SimpleDateFormat sdf = new SimpleDateFormat(format);
        try {
            return sdf.parse(date);
        } catch (ParseException e) {
            log.error(":>>> 解析时间错误:{}", e.getMessage());
            return null;
        }
    }

    public static boolean between(Date startTime, Date endTime, Date d) {
        log.info(":>>> 开始时间:{},在区间[{},{}]内", d, startTime, endTime);
        if (startTime == null || startTime == null || Objects.isNull(d)) {
            throw new IllegalArgumentException("between param is null!");
        }
        if ((d.after(startTime) || d.equals(startTime)) && (d.before(endTime) || d.equals(endTime))) {
            return true;
        }
        return false;
    }

    /**
     * 判断两个日期是否挨着的连续天,传入参数d1<d2
     *
     * @param d1
     * @param d2
     * @return
     */
    public static boolean isNotStop(Date d1, Date d2) {
        log.info(":>>> 开始判断时间:{} 和时间:{} 是否连续", d1, d2);
        if (Objects.isNull(d1) || Objects.isNull(d2)) {
            return true;
        }
        if (Objects.isNull(d1) && Objects.isNull(d2)) {
            log.error(":>>> 两位时间都为空,异常");
            throw new IllegalArgumentException("isNotStop param is null!");
        }

        if (getDiffDays(d1, d2) < 1) {
            log.info(":>>> 时间:{},和时间:{} 是连续时间", d1, d2);
            return true;
        }
        return false;
    }

    /**
     * //创建SimpleDateFormat对象实例并定义好转换格式
     *
     * @param dateStr
     * @param format
     * @return
     */
    public static Date parse(String dateStr, String format) {
        log.info(":>>> 开始解析日期:{},{}", dateStr, format);
        //"yyyy-MM-dd HH:mm:ss"
        SimpleDateFormat sdf = new SimpleDateFormat(format);

//        System.out.println("把当前时间转换成字符串：" + sdf.format(new Date()));

        Date date = null;
        try {
            // 注意格式需要与上面一致，不然会出现异常
            date = sdf.parse(dateStr);
            return date;
        } catch (ParseException e) {
            log.error(":>>> 解析时间错误:{}", e.getMessage());
            return null;
        }
    }

    /**
     * 解析时间
     *
     * @param date
     * @param format
     * @return
     */
    public static String parse(Date date, String format) {
        String dateStr = new SimpleDateFormat(format).format(date);
        return dateStr;
    }

    /**
     * 按照指定的格式返回日期字符串. 默认 "yyyy-MM-dd "
     *
     * @param date
     * @param pattern
     * @return
     */
    public static String formatDate(Date date, String pattern) {
        if (date == null) {
            return "";
        }
        if (pattern == null) {
            pattern = "yyyy-MM-dd";
        }
        SimpleDateFormat sdf = new SimpleDateFormat(pattern);
        return (sdf.format(date));
    }

    /**
     * 获取当前时间年月日字符串，例如:20220423,结果为:220423
     *
     * @param date
     * @return
     */
    public static String getCurrentYMD(Date date) {
        String s = formatDate(date, "yyyy-MM-dd");
        s = s.replace("-", "");
        s = s.substring(2);
        return s;
    }

    /**
     * 计算日期之间工作日的小时数
     */
    public static long getWorkHours() throws ParseException {

        // 当前时间
        Date now = new Date();

        // 截止时间
        String line = "2022-10-26 18:30:30";

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        Date lineDate = sdf.parse(line);

        // 工作日时间8小时
        int workDayHours = 24;

        DateTime beginTime = DateTime.of(now);
        DateTime endTime = DateTime.of(lineDate);

        // 开始日期的上班时间和下班时间
        Date beginStartWork = sdf.parse(beginTime.toDateStr() + " 00:00:00");
        Date beginAfterWork = sdf.parse(beginTime.toDateStr() + " 23:59:59");

        // 截止日期的上班时间和下班时间
        Date endStartWork = sdf.parse(endTime.toDateStr() + " 00:00:00");
        Date endAfterWork = sdf.parse(endTime.toDateStr() + " 23:59:59");

        long totalWorkHours = 0;
        // 计算两个日期之间的天数差
        long daySub = DateUtil.between(DateUtil.truncate(now, DateField.DAY_OF_YEAR), DateUtil.truncate(lineDate, DateField.DAY_OF_YEAR), DateUnit.DAY, false);
        if (daySub > 1) {
            int weekendDays = 0;
            // 取两个日期的当天0点作为计算天，计算两个日期之间的周末的天数
            DateTime start = DateUtil.truncate(now, DateField.DAY_OF_YEAR);
            DateTime end = DateUtil.truncate(lineDate, DateField.DAY_OF_YEAR);
            while (DateUtil.between(start, end, DateUnit.DAY, false) > 1) {
                if (start.isWeekend()) {
                    weekendDays++;
                }
                start = start.offset(DateField.DAY_OF_YEAR, 1);
            }
            // 计算两个日期之间的整工作日，排除前后两个日期
            long workDays = (daySub - weekendDays - 2) < 0 ? 0 : (daySub - weekendDays - 2);
            totalWorkHours = workDays * workDayHours;
            // 计算前后两个工作日的工作小时数
            if (!beginTime.isWeekend()) {
                totalWorkHours += (now.before(beginStartWork)) ? 8 : (now.after(beginAfterWork) ? 0 : DateUtil.between(beginTime, beginAfterWork, DateUnit.HOUR, false));
            }
            if (!endTime.isWeekend()) {
                totalWorkHours += (lineDate.before(endStartWork)) ? 0 : (lineDate.after(endAfterWork) ? 8 : DateUtil.between(endStartWork, lineDate, DateUnit.HOUR, false));
            }
        } else if (daySub == 1) {
            // 计算前后两个工作日的工作小时数
            if (!beginTime.isWeekend()) {
                totalWorkHours += (now.before(beginStartWork)) ? 8 : (now.after(beginAfterWork) ? 0 : DateUtil.between(beginTime, beginAfterWork, DateUnit.HOUR, false));
            }
            if (!endTime.isWeekend()) {
                totalWorkHours += (lineDate.before(endStartWork)) ? 0 : (lineDate.after(endAfterWork) ? 8 : DateUtil.between(endStartWork, lineDate, DateUnit.HOUR, false));
            }
        } else {
            if (now.before(beginStartWork) && lineDate.after(beginAfterWork)) {
                totalWorkHours = 8;
            } else if (now.after(beginStartWork) && lineDate.after(beginAfterWork)) {
                totalWorkHours = DateUtil.between(now, beginAfterWork, DateUnit.HOUR, false);
            } else if (now.before(beginStartWork) && lineDate.before(beginAfterWork)) {
                totalWorkHours = DateUtil.between(beginStartWork, lineDate, DateUnit.HOUR, false);
            } else if (now.after(beginStartWork) && lineDate.before(beginAfterWork)) {
                totalWorkHours = DateUtil.between(now, lineDate, DateUnit.HOUR, false);
            }
        }

        System.out.println("当前时间[ " + beginTime + " ]到截止时间[ " + endTime + " ]的小时数：" + totalWorkHours);
        return totalWorkHours;
    }

    /**
     * 日期之前的小时数
     *
     * @param startDate
     * @param endData
     * @return
     */
    public static Integer getHours(Date startDate, Date endData) {
        log.info(":>>> 开始获取开始时间:{},结束时间:{} 之间小时数", startDate, endData);
        Double s = Math.ceil((endData.getTime() - startDate.getTime()) / 3600000L);
        return s.intValue();
    }

    /**
     * Date 转换成localDate
     *
     * @param date
     * @return
     */
    public static LocalDate toLocalDate(Date date) {
        LocalDate localDate = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
        return localDate;
    }
    /**
     * 将 LocalDate 转为 Date
     *
     * @param localDate
     * @return java.util.Date
     */
    public static Date localDateToDate(LocalDate localDate) {
        return Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
    }

}
